import * as bfun from '../../core/fbyte.js';
import BNode from './bnode1.js';

/*
 自研压缩算法,小于1024字节无需压缩
	通用压缩法:适用于无法确定内容的压缩场景
	如果经常出现多个相同字节靠在一起 ,需要用其他的压缩算法
*/
function HZip(bc) {
	let root = BNode.newCreate();
	let 最后字 = null;
	let 步长 = bc || 8; //步长就是缓存
	let 块 = 0;
	//测试
	function set(key) {
		if (key) {
			key = key.subarray(0, 步长);
			BNode.setn(root, key);
		}
	}
	/**
	 * 添加一个字节流
	 * @param {Uint8Array} bytes
	 */
	let add = function(bytes) {
		root = BNode.newCreate();
		//预估步长			
		块 = 0;
		最后字 = foreach(bytes, (ui) => {
			//console.log(ui)
			BNode.setn(root, ui);
			块++
		});
		//console.log(最后字)
	}
	//遍历所有字节
	let foreach = function(bytes, callback) {
		let i = 0;
		while (i + 步长 < bytes.length) {
			let ui = bytes.subarray(i, i + 步长);
			callback(ui);
			i += 步长;
		}
		if (i < bytes.length) {
			let ui = bytes.subarray(i);
			return ui;
		}
		return null;
	}
	//统计项数
	let _Table = function() {
		let cnt = 0;
		let arr = new Array();
		//let mp = BNode.newCreate();
		BNode.down(root, function(pnd,nd, b) {	
			if (nd.child.size == 0) {
				if (nd.level == 步长) {
					// 编号,转码 必须等到cnt 确认才能转码, 源编码
					arr.push(nd.code);
					cnt++;
				}
				return true;
			}
		});
		//console.log(arr)
		let u8i = new Uint8Array(cnt * 步长);
		arr.sort(function(a, b) {
			return bfun.compareBytes(a[0], b[0])
		});
		//console.log(arr);
		let 字长 = 0;
		let n = 0;
		let mp = new Map();
		let f = null;
		if (cnt < 256) {
			字长 = 1;
			f = function(a) {
				return new Uint8Array([a]);
			};
		} else if (cnt < 65535) {
			字长 = 2;
			f = bfun.shortToUi8;
		} else {
			字长 = 4;
			f = bfun.intToUi8;
		}
		arr.forEach(function(item, ind) {
			u8i.set(item, n);
			let ii = f(ind);
			mp.set(item.toString(), [item, ii]);
			n += 步长;
		});
		//console.log(mp);
		return {
			cnt,
			u8i,
			n,
			mp,
			字长
		};
	}

	let ui8_table = function(bytes, 步长, 字长) {
		let mp = new Map();

		let f = null;
		if (字长 == 1) {
			f = function(a) {
				return new Uint8Array([a]);
			};
		} else if (字长 == 2) {
			f = bfun.shortToUi8;
		} else {
			f = bfun.intToUi8;
		}
		let i = 0,
			k = 0;
		//console.log(bytes)
		while (i < bytes.length) {
			let key = f(k);
			let value = bytes.subarray(i, i + 步长);
			//console.log(key,value)
			i += 步长;
			mp.set(key.toString(), [value, key]);
			k++;
		}
		//console.log(mp)
		return mp;
	}
	this.zip = function(bytes, vbc) {
		add(bytes, vbc);
		//console.log("根", root);
		//let uilist=BNode.treeToList(root);
		//console.log(uilist);
		//let root2=BNode.listToTree(uilist);
		//console.log(root2)		
		let tb = _Table();
		let u82 = new Uint8Array(11 + tb.n);
		u82.set([1], 0); //可压缩
		//步长
		u82.set([步长], 1);
		//字长
		u82.set([tb.字长], 2);
		//数据块	
		u82.set(bfun.intToUi8(块), 3);
		//表长
		u82.set(bfun.intToUi8(tb.n), 7);
		//表字典		
		u82.set(tb.u8i, 11);
		//console.log(tb);
		let u81 = new Uint8Array(1 + bytes.length);
		//替换后字符
		let n = 0;
	//console.log( tb.mp);
		foreach(bytes,
			function(s1) {
				//if(tb.mp.size==0) return;
				let rep = tb.mp.get(s1.toString());
				//console.log(u81,s1,rep)
				u81.set(rep[1], n);
				n += tb.字长;
			}
		);
		u81.set(最后字, n);
		n += 最后字.length;
		return bfun.concatUi8Arr(u82, u81.subarray(0, n))
	}

	this.unzip = function(bytes) {
		//如果不压缩直接输出
		if (bytes[0] == 0) return bytes.slice(1);
		步长 = bytes[1];
		let 字长 = bytes[2];
		//console.log(字长)
		let 块 = bfun.byte4ToInt(bytes, 3);
		let 表长 = bfun.byte4ToInt(bytes, 7);
		let n = 11 + 表长;
		//表
		let mp = ui8_table(bytes.subarray(11, n), 步长, 字长);
		//console.log(mp)
		//数据
		let u82 = new Uint8Array((块 + 1) * 步长);
		let i = 0;
		let j = 0;
		while (i < 块) {
			let key = bytes.subarray(n, n + 字长).toString();
			n += 字长;
			i++;
			let rep = mp.get(key);
			//console.log(key,rep)
			u82.set(rep[0], j);
			j += 步长;
		}
		if (n < bytes.length) {
			let uc = bytes.subarray(n);
			u82.set(uc, j);
			j += uc.length;
		}
		return u82.slice(0, j);
	}
	this.show = function() {
		return root;
	}
}
export default HZip;
